package io

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"time"

	"github.com/gorilla-go/container"
	"github.com/gorilla-go/pig/constant"
)

type Output struct {
	response http.ResponseWriter
	body     []byte
	code     int
	headers  http.Header
	cookies  []*http.Cookie
	file     string
	fileName string
	rendered bool
}

// 渲染响应
func (o *Output) Render() {
	defer func() {
		o.rendered = true
	}()

	// 设置响应头
	for key, values := range o.headers {
		for _, value := range values {
			o.response.Header().Add(key, value)
		}
	}

	// 设置 Cookie
	for _, cookie := range o.cookies {
		http.SetCookie(o.response, cookie)
	}

	// 输出文件
	if o.file != "" {
		file, err := os.Open(o.file)
		if err != nil {
			panic(err)
		}
		defer file.Close()

		// 获取文件信息
		fileInfo, err := file.Stat()
		if err != nil {
			panic(err)
		}

		if o.fileName == "" {
			o.fileName = fileInfo.Name()
		}

		// 设置文件响应头
		o.response.Header().Set("Content-Type", "application/octet-stream")
		o.response.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", o.fileName))
		o.response.Header().Set("Content-Length", fmt.Sprintf("%d", fileInfo.Size()))

		o.response.WriteHeader(o.code)
		io.Copy(o.response, file)
		return
	}

	// 输出响应体
	o.response.WriteHeader(o.code)
	o.response.Write(o.body)
}

// 是否已渲染
func (o *Output) IsRendered() bool {
	return o.rendered
}

// 设置状态码
func (o *Output) Code(code int) *Output {
	o.code = code
	return o
}

// 设置响应头
func (o *Output) SetHeader(key, value string) *Output {
	o.headers.Set(key, value)
	return o
}

// 添加响应头
func (o *Output) AddHeader(key, value string) *Output {
	o.headers.Add(key, value)
	return o
}

// 添加 Cookie
func (o *Output) AddCookie(cookie *http.Cookie) *Output {
	o.cookies = append(o.cookies, cookie)
	return o
}

// 设置 Cookies
func (o *Output) SetCookieKV(key, value string, expires time.Duration) *Output {
	for _, cookie := range o.cookies {
		if cookie.Name == key {
			cookie.Value = value
			cookie.Expires = time.Now().Add(expires)
			return o
		}
	}

	return o.AddCookieKV(key, value, expires)
}

// 添加 Cookie KV
func (o *Output) AddCookieKV(key, value string, expires time.Duration) *Output {
	o.cookies = append(o.cookies, &http.Cookie{
		Name:    key,
		Value:   value,
		Path:    "/",
		Expires: time.Now().Add(expires),
	})
	return o
}

// 输出文本
func (o *Output) Echo(context []byte) {
	o.headers.Set("Content-Type", "text/plain; charset=utf-8")
	o.body = context
}

// 输出 XML
func (o *Output) Xml(context []byte) {
	o.headers.Set("Content-Type", "application/xml; charset=utf-8")
	o.body = context
}

// 输出 Html
func (o *Output) Html(context []byte) {
	o.headers.Set("Content-Type", "text/html; charset=utf-8")
	o.body = context
}

// 输出 Json
func (o *Output) Json(context any) {
	o.headers.Set("Content-Type", "application/json; charset=utf-8")
	body, err := json.Marshal(context)
	if err != nil {
		panic(err)
	}
	o.body = body
}

// 输出文件
func (o *Output) Download(path string, fileName string) {
	o.file = path
	o.fileName = fileName
}

// 获取输出
func GetOutput(ctx context.Context) *Output {
	return container.GetMustSingletonByAlias[*Output](
		ctx.Value(constant.Container).(*container.Container),
		constant.Output,
	)
}

// 输出格式化器
var OutputFormatter = func(ctx context.Context, next func(ctx context.Context)) {
	c := ctx.Value(constant.Container).(*container.Container)
	response := container.GetMustImplement[http.ResponseWriter](c)
	output := &Output{
		response: response,
		headers:  make(http.Header),
		cookies:  make([]*http.Cookie, 0),
		code:     http.StatusOK,
		body:     make([]byte, 0),
	}
	container.SetSingletonWithAlias(c, constant.Output, output)
	next(ctx)
	output.Render()
}
